/**
    @file pixelworld.cpp
    @brief Game world
*/
#include "pixel/pixelworld.hpp"
#include "lua/lua_pixelworld.hpp"

template<> pm3::PIXELWORLD* Ogre::Singleton<pm3::PIXELWORLD>::ms_Singleton = 0;
pm3::PIXELWORLD pm3::g_world;

pm3::PIXELWORLD::PIXELWORLD() {
    m_cur_map = -1;
}

pm3::PIXELWORLD::~PIXELWORLD() {
    m_maps.clear();
    m_cur_map = -1;
}

void pm3::PIXELWORLD::init() {
    pm3::INTERFACE *intf = pm3::INTERFACE::getSingletonPtr();
    static bool done = false;

    if (done)
        return;

    // Set active player pointer
    g_player = &m_player;
    // Initialize player node structure
    m_player.init();

    // Make sure there's a world node to which the maps can be attached
    intf->m_world_node = 
        (Ogre::PCZSceneNode *) intf->get_scenemgr()->getRootSceneNode()->createChildSceneNode( "PixelWorldNode" );
    // Make the world node as an inhabitant of the default zone
    intf->get_world_node()->setHomeZone( intf->get_scenemgr()->getDefaultZone() );
    intf->get_scenemgr()->getDefaultZone()->_addNode( intf->get_world_node() );
    // Initalize portal debug drawer
    pm3::dbg::PORTAL_DRAWER::getSingleton().init( intf->get_world_node() );

    // Create a zone for hiding objects
    intf->get_scenemgr()->createZone( "ZoneType_Default", PM_HIDEZONE );

    // Set up player physics
    m_player.setup_body();

    // Set active map pointer
    pm3::lua::g_active_map = &m_map;

    done = true;
}

void pm3::PIXELWORLD::bind() {
    tolua_worldscript_open( m_lua_state );
}

void pm3::PIXELWORLD::unload() {
    m_cur_map = -1;
    m_map.unload();
    m_maps.clear();
}

bool pm3::PIXELWORLD::select_map( int i_num ) {
    LOGGER *log = LOGGER::getSingletonPtr();
    Ogre::ViewPoint startpoint;
    Ogre::PCZone *zone = NULL;

    // Is there a next map?
    if (i_num >= 0 && i_num < (int) m_maps.size()) {
        // Unload the previous map
        m_map.unload();
        // Set map indexes
        m_cur_map = i_num;
        m_map.m_id = i_num;

        if (log)
            log->report( "Pixel", "I: Selecting map number %d (\"%s\")\n", 
                         i_num, m_maps [i_num].c_str() );

        // And load the mapscript
        pm3::lua::g_active_map = &m_map;
        if (!m_map.load( get_directory() + m_maps [m_cur_map] ))
            return false;

        if (log)
            log->report( "Pixel", "I: Initializing map %d (\"%s\")\n", 
                         i_num, m_maps [i_num].c_str() );

        m_map.init();

        // Find a player start position
        startpoint = m_map.find_start();
        // Init player (if not already done)
        init();
        // Set player zone, position and orientation
        zone = m_map.get_zone( startpoint.position.y, startpoint.position.x, startpoint.position.z );

        if (zone) {
            // Activate player physics
            m_player.set_physics_active( true );
            // Teleport player
            m_player.teleport( m_map.grid_to_world( startpoint.position ), zone );
            // Set player orientation
            m_player.set_orientation( startpoint.orientation );

            if (log)
                log->report( "Pixel", "I: Player snapped to startpoint (%f, %f, %f)\n", 
                             startpoint.position.x, startpoint.position.y, startpoint.position.z );
        } else {
            if (log)
                log->report( "Pixel", "E: No zone at (%f, %f, %f) - probably a layer image and coordinate offset mismatch\n",
                             startpoint.position.x, startpoint.position.y, startpoint.position.z );
        }

        return true;
    }

    if (log)
        log->report( "Pixel", "E: There is no map number %d\n", i_num );

    return false;
}

bool pm3::PIXELWORLD::next_map() {
    return select_map( m_cur_map + 1 );
}

bool pm3::PIXELWORLD::prev_map() {
    return select_map( m_cur_map - 1 );
}

void pm3::PIXELWORLD::update() {
    debug_render();

    // Any map loaded? Update it
    if (m_cur_map >= 0)
        m_map.update( m_player.get_grid_pos() );

    // Are there any messages?
    process_messages();
}

void pm3::PIXELWORLD::debug_render() {
    m_map.debug_render();
    pm3::dbg::PORTAL_DRAWER::getSingleton().step();
}

void pm3::PIXELWORLD::process_messages() {
    PIXELPOST post;

    do {
        // Take the next post in the mailing list
        post = pm3::g_mail.getSingleton().take();
        // Next map message?
        if (post.get_type() == PM_NEXT_MAP)
            next_map();
    } while (post.get_type() != PM_NO_MAIL);
}

int pm3::PIXELWORLD::add_map( const std::string &r_path ) {
    m_maps.push_back( r_path );
    return ( m_maps.size() - 1 );
}

void pm3::PIXELWORLD::dump() const {
    LOGGER *log = LOGGER::getSingletonPtr();

    if (log) {
        log->report( "Pixel", "I: World dump:\n" );

        for (int i=0; i<(int)m_maps.size(); i++) {
            if (i == m_cur_map)
                log->report( "Pixel", " > Map %d = \"%s\"\n", i, m_maps [i].c_str() );
            else
                log->report( "Pixel", "   Map %d = \"%s\"\n", i, m_maps [i].c_str() );
        }
    }
}

